package com.gitee.dufafei.plugin.variable;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class VariableUtils {

    public static final String UNIX_OPEN = "${";
    public static final String UNIX_CLOSE = "}";
    public static final String WINDOWS_OPEN = "%%";
    public static final String WINDOWS_CLOSE = "%%";
    public static final String HEX_OPEN = "$[";
    public static final String HEX_CLOSE = "]";

    /**
     * ${AA} Fetch the value of the variable AA
     * %%AA%% Fetch the value of the variable AA
     * $[AA] Example Change AA to hexadecimal
     */
    public static synchronized String environmentSubstitute(String aString, Map<String, String> properties) {
        Map<String, String> sysMap = new HashMap<>();
        synchronized (Collections.unmodifiableMap(sysMap)) {
            sysMap.putAll(Collections.synchronizedMap(properties));
            aString = substituteWindows(aString, sysMap);
            aString = substituteUnix(aString, sysMap);
            aString = substituteHex(aString);
            return aString;
        }
    }

    public static String substituteWindows(String aString, Map<String, String> variables) {
        return substitute(aString, variables, WINDOWS_OPEN, WINDOWS_CLOSE);
    }

    public static String substituteUnix(String aString, Map<String, String> variables) {
        return substitute(aString, variables, UNIX_OPEN, UNIX_CLOSE);
    }

    public static String substitute(String aString, Map<String, String> variablesValues, String open, String close) {
        return substitute(aString, variablesValues, open, close, 0);
    }

    public static String substitute(String aString, Map<String, String> variablesValues, String open, String close, int recursion) {
        if (aString == null) {
            return null;
        }
        StringBuilder buffer = new StringBuilder();
        String rest = aString;
        // search for opening string
        int i = rest.indexOf(open);
        while (i > -1) {
            int j = rest.indexOf(close, i + open.length());
            // search for closing string
            if (j > -1) {
                String varName = rest.substring(i + open.length(), j);
                Object value = variablesValues.get(varName);
                if (value == null) {
                    value = open + varName + close;
                } else {
                    // check for another variable inside this value
                    int another = ((String) value).indexOf(open); // check
                    // here
                    // first for
                    // speed
                    if (another > -1) {
                        // for safety: avoid recursive
                        if (recursion > 50) {
                            // endless loops with stack overflow
                            throw new RuntimeException("Endless loop detected for substitution of variable: " + value);
                        }
                        value = substitute((String) value, variablesValues, open, close, ++recursion);
                    }
                }
                buffer.append(rest, 0, i);
                buffer.append(value);
                rest = rest.substring(j + close.length());
            } else {
                // no closing tag found; end the search
                buffer.append(rest);
                rest = "";
            }
            // keep searching
            i = rest.indexOf(open);
        }
        buffer.append(rest);
        return buffer.toString();
    }

    public static String substituteHex(String aString) {
        if (aString == null) {
            return null;
        }
        StringBuilder buffer = new StringBuilder();
        String rest = aString;
        // search for opening string
        int i = rest.indexOf(HEX_OPEN);
        while (i > -1) {
            int j = rest.indexOf(HEX_CLOSE, i + HEX_OPEN.length());
            // search for closing string
            if (j > -1) {
                buffer.append(rest, 0, i);
                String hexString = rest.substring(i + HEX_OPEN.length(), j);
                String[] hexStringArray = hexString.split(",");
                int hexInt;
                byte[] hexByte = new byte[1];
                for (String s : hexStringArray) {
                    try {
                        hexInt = Integer.parseInt(s, 16);
                    } catch (NumberFormatException e) {
                        // in case we get an invalid hex value, ignore: we can not log here
                        hexInt = 0;
                    }
                    hexByte[0] = (byte) hexInt;
                    buffer.append(new String(hexByte));
                }
                rest = rest.substring(j + HEX_CLOSE.length());
            } else {
                // no closing tag found; end the search
                buffer.append(rest);
                rest = "";
            }
            // keep searching
            i = rest.indexOf(HEX_OPEN);
        }
        buffer.append(rest);
        return buffer.toString();
    }
}
